package com.itlong.cloud.utils.node;

import com.itlong.cloud.thrown.AesException;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;
import java.lang.reflect.Field;
import java.util.*;

/**
 * <desc>
 *      XML转换操作
 * <desc/>
 *
 * @craeteDate 2017/9/21
 */
public class XMLParser {


    protected static final Logger LOG = LoggerFactory.getLogger(XMLParser.class);

    /**
     * <desc>
     *      将XML转换成Map.
     * <desc/>
     *
     * @param xml
     * @param rootElement 根节点
     * @return Map结构数据
     * @author Wenchao.L
     * @createDate 2017/9/21
     */
    public static Map getMapFromXML(String xml, String rootElement) throws DocumentException {
        org.dom4j.Document doc = DocumentHelper.parseText(xml);
        Element body = (Element) doc.selectSingleNode("/" + rootElement);
        Map vo = buildXmlBody2map(body);
        return vo;
    }


    /**
     * <desc>
     *      转换字符串.
     * <desc/>
     *
     * @param object
     * @return String
     * @author Wenchao.L
     * @createDate 2017/9/21
     */
    public static String coverter(Object object) {
        if (object instanceof Object[]) {
            return coverter((Object[]) object);
        }
        if (object instanceof Collection) {
            return coverter((Collection<?>) object);
        }
        StringBuilder strBuilder = new StringBuilder();
        if (isObject(object)) {
            Class<? extends Object> clz = object.getClass();
            Field[] fields = clz.getDeclaredFields();

            for (Field field : fields) {
                field.setAccessible(true);
                if (field == null) {
                    continue;
                }
                String fieldName = field.getName();
                Object value = null;
                try {
                    value = field.get(object);
                } catch (IllegalArgumentException e) {
                    continue;
                } catch (IllegalAccessException e) {
                    continue;
                }
                strBuilder.append("<").append(fieldName)
                        .append(" className=\"").append(
                        value.getClass().getName()).append("\">\n");
                if (isObject(value)) {
                    strBuilder.append(coverter(value));
                } else if (value == null) {
                    strBuilder.append("null\n");
                } else {
                    strBuilder.append(value.toString() + "\n");
                }
                strBuilder.append("</").append(fieldName).append(">\n");
            }
        } else if (object == null) {
            strBuilder.append("null\n");
        } else {
            strBuilder.append(object.toString() + "\n");
        }
        return strBuilder.toString();
    }


    /**
     * <desc>
     *      判断节点类型.
     * <desc/>
     *
     * @param body 节点
     * @return Map
     * @author Wenchao.L
     * @createDate 2017/9/21
     */
    private static Map buildXmlBody2map(Element body) {
        Map vo = new HashMap();
        if (body != null) {
            List<Element> elements = body.elements();
            for (Element element : elements) {
                String key = element.getName();
                if (StringUtils.isNotEmpty(key)) {
                    String type = element.attributeValue("type", "java.lang.String");
                    String text = element.getText().trim();
                    Object value = null;
                    if (String.class.getCanonicalName().equals(type)) {
                        value = text;
                    } else if (Character.class.getCanonicalName().equals(type)) {
                        value = new Character(text.charAt(0));
                    } else if (Boolean.class.getCanonicalName().equals(type)) {
                        value = new Boolean(text);
                    } else if (Short.class.getCanonicalName().equals(type)) {
                        value = Short.parseShort(text);
                    } else if (Integer.class.getCanonicalName().equals(type)) {
                        value = Integer.parseInt(text);
                    } else if (Long.class.getCanonicalName().equals(type)) {
                        value = Long.parseLong(text);
                    } else if (Float.class.getCanonicalName().equals(type)) {
                        value = Float.parseFloat(text);
                    } else if (Double.class.getCanonicalName().equals(type)) {
                        value = Double.parseDouble(text);
                    } else if (java.math.BigInteger.class.getCanonicalName().equals(type)) {
                        value = new java.math.BigInteger(text);
                    } else if (java.math.BigDecimal.class.getCanonicalName().equals(type)) {
                        value = new java.math.BigDecimal(text);
                    } else if (Map.class.getCanonicalName().equals(type)) {
                        value = buildXmlBody2map(element);
                    } else {
                    }
                    vo.put(key, value);
                }
            }
        }
        return vo;
    }


    /**
     * <desc>
     *      将Map转换成XML.
     * <desc/>
     *
     * @param dataMap 转换MAP
     * @return XML
     * @author Wenchao.L
     * @createDate 2017/9/21
     */
    public static String converterPayPalm(Map<Object, Object> dataMap) {
        StringBuilder strBuilder = new StringBuilder();
        try {
            strBuilder.append("<xml>");
            Set<Object> objSet = dataMap.keySet();
            for (Object key : objSet) {
                if (key == null) {
                    continue;
                }
                //strBuilder.append("\n");
                strBuilder.append("<").append(key.toString()).append(">");
                Object value = dataMap.get(key);
                strBuilder.append(coverter(value).trim());
                strBuilder.append("</").append(key.toString()).append(">");
            }
            strBuilder.append("</xml>");
        } catch (Exception e) {
            LOG.error("MAP转换XML异常：" + e);
        }
        return strBuilder.toString();
    }


    /**
     * <desc>
     *      判断是否是对象.
     * <desc/>
     *
     * @param obj
     * @return boolean
     * @author Wenchao.L
     * @createDate 2017/9/21
     */
    private static boolean isObject(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj instanceof String) {
            return false;
        }
        if (obj instanceof Integer) {
            return false;
        }
        if (obj instanceof Double) {
            return false;
        }
        if (obj instanceof Float) {
            return false;
        }
        if (obj instanceof Byte) {
            return false;
        }
        if (obj instanceof Long) {
            return false;
        }
        if (obj instanceof Character) {
            return false;
        }
        if (obj instanceof Short) {
            return false;
        }
        if (obj instanceof Boolean) {
            return false;
        }
        return true;
    }

    /**
     * Xml string转换成Map
     * @param xmlStr
     * @return
     */
    public static Map<Object,Object> xmlString2Map(String xmlStr){
        Map<Object,Object> map = new HashMap<Object,Object>();
        Document doc;
        try {
            doc = DocumentHelper.parseText(xmlStr);
            Element el = doc.getRootElement();
            map = recGetXmlElementValue(el,map);
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return map;
    }

    /**
     * 循环解析xml
     * @param ele
     * @param map
     * @return
     */
    @SuppressWarnings({ "unchecked" })
    private static Map<Object, Object> recGetXmlElementValue(Element ele, Map<Object, Object> map){
        List<Element> eleList = ele.elements();
        if (eleList.size() == 0) {
            map.put(ele.getName(), ele.getTextTrim());
            return map;
        } else {
            for (Iterator<Element> iter = eleList.iterator(); iter.hasNext();) {
                Element innerEle = iter.next();
                recGetXmlElementValue(innerEle, map);
            }
            return map;
        }
    }

    /**
     * 提取出xml数据包中的加密消息(接收微信推送的ticket)
     * @param xmltext 待提取的xml字符串
     * @return 提取出的加密消息字符串
     * @throws AesException
     */
    public static Object[] extract(String xmltext) throws AesException {
        Object[] result = new Object[3];
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
            dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
            dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
            dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
            dbf.setXIncludeAware(false);
            dbf.setExpandEntityReferences(false);
            DocumentBuilder db = dbf.newDocumentBuilder();
            StringReader sr = new StringReader(xmltext);
            InputSource is = new InputSource(sr);
            org.w3c.dom.Document document = db.parse(is);

            org.w3c.dom.Element root = document.getDocumentElement();
            NodeList nodelist1 = root.getElementsByTagName("Encrypt");
            NodeList nodelist2 = root.getElementsByTagName("AppId");
            result[0] = 0;
            result[1] = nodelist1.item(0).getTextContent();
            result[2] = nodelist2.item(0).getTextContent();
            return result;
        } catch (Exception e) {
            LOG.info(e + "");
            throw new AesException(AesException.ParseXmlError);
        }
    }
}
